/**
 * MOAB, a Mesh-Oriented datABase, is a software component for creating,
 * storing and accessing finite element mesh data.
 *
 * Copyright 2004 Sandia Corporation.  Under the terms of Contract
 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
 * retains certain rights in this software.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 */

//-------------------------------------------------------------------------
// Filename      : WriteSLAC.hpp
//
// Purpose       : ExodusII writer
//
// Special Notes : Lots of code taken from verde implementation
//
// Creator       : Corey Ernst
//
// Date          : 8/02
//
// Owner         : Corey Ernst
//-------------------------------------------------------------------------

#ifndef WRITESLAC_HPP
#define WRITESLAC_HPP

#ifndef IS_BUILDING_MB
#error "WriteSLAC.hpp isn't supposed to be included into an application"
#endif

#include <vector>
#include <string>

#include "moab/Forward.hpp"
#include "moab/Range.hpp"
#include "moab/ExoIIInterface.hpp"
#include "moab/WriterIface.hpp"

namespace moab
{

class WriteUtilIface;

class WriteSLAC : public WriterIface
{

  public:
    //! Constructor
    WriteSLAC( Interface* impl );

    //! Destructor
    virtual ~WriteSLAC();

    static WriterIface* factory( Interface* );

    //! writes out a file
    ErrorCode write_file( const char* file_name, const bool overwrite, const FileOptions& opts,
                          const EntityHandle* output_list, const int num_sets,
                          const std::vector< std::string >& qa_list, const Tag* tag_list = NULL, int num_tags = 0,
                          int export_dimension = 3 );

    //! struct used to hold data for each block to be output; used by
    //! initialize_file to initialize the file header for increased speed
    struct MaterialSetData
    {
        int id;
        int number_elements;
        int number_nodes_per_element;
        int number_attributes;
        ExoIIElementType element_type;
        EntityType moab_type;
        Range* elements;
    };

    //! struct used to hold data for each nodeset to be output; used by
    //! initialize_file to initialize the file header for increased speed
    struct DirichletSetData
    {
        int id;
        int number_nodes;
        std::vector< EntityHandle > nodes;
        std::vector< double > node_dist_factors;
    };

    //! struct used to hold data for each sideset to be output; used by
    //! initialize_file to initialize the file header for increased speed
    struct NeumannSetData
    {
        int id;
        int number_elements;
        std::vector< EntityHandle > elements;
        std::vector< int > side_numbers;
        EntityHandle mesh_set_handle;
    };

  protected:
    //! number of dimensions in this file
    // int number_dimensions();

    //! open a file for writing
    ErrorCode open_file( const char* filename );

    //! contains the general information about a mesh
    class MeshInfo
    {
      public:
        unsigned int num_dim;
        unsigned int num_nodes;
        unsigned int num_elements;
        unsigned int num_matsets;
        unsigned int num_int_hexes;
        unsigned int num_int_tets;
        Range bdy_hexes, bdy_tets;
        Range nodes;

        MeshInfo()
            : num_dim( 0 ), num_nodes( 0 ), num_elements( 0 ), num_matsets( 0 ), num_int_hexes( 0 ), num_int_tets( 0 )
        {
        }
    };

  private:
    //! interface instance
    Interface* mbImpl;
    WriteUtilIface* mWriteIface;

    //! file name
    std::string fileName;
    int ncFile;

    //! Cached tags for reading.  Note that all these tags are defined when the
    //! core is initialized.
    Tag mMaterialSetTag;
    Tag mDirichletSetTag;
    Tag mNeumannSetTag;
    Tag mGlobalIdTag;
    Tag mMatSetIdTag;

    Tag mEntityMark;  // used to say whether an entity will be exported

    ErrorCode gather_mesh_information( MeshInfo& mesh_info, std::vector< MaterialSetData >& matset_info,
                                       std::vector< NeumannSetData >& neuset_info,
                                       std::vector< DirichletSetData >& dirset_info,
                                       std::vector< EntityHandle >& matsets, std::vector< EntityHandle >& neusets,
                                       std::vector< EntityHandle >& dirsets );

    ErrorCode initialize_file( MeshInfo& mesh_info );

    ErrorCode write_nodes( const int num_nodes, const Range& nodes, const int dimension );

    ErrorCode write_matsets( MeshInfo& mesh_info, std::vector< MaterialSetData >& matset_data,
                             std::vector< NeumannSetData >& neuset_data );

    ErrorCode get_valid_sides( Range& elems, const int sense, WriteSLAC::NeumannSetData& sideset_data );

    void reset_matset( std::vector< MaterialSetData >& matset_info );

    ErrorCode get_neuset_elems( EntityHandle neuset, int current_sense, Range& forward_elems, Range& reverse_elems );

    ErrorCode gather_interior_exterior( MeshInfo& mesh_info, std::vector< MaterialSetData >& matset_data,
                                        std::vector< NeumannSetData >& neuset_data );
};

}  // namespace moab

#endif
